package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.web;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.PageInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.JsonUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.ModelUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.StringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ResponseResult;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.ModelVersionDetailDTO;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowDeploySendMessageRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowEventConfigRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowModelRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.utils.EncodingUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author lilh
 * @date 2019-01-20 13:21
 */
@Api(value = "模型管理", tags = "模型管理")
@RestController
@RequestMapping("/model")
public class FlowModelController {
    private static final Logger logger = LoggerFactory.getLogger(FlowModelController.class);

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FlowModelRepository flowModelRepository;

    @Autowired
    private FlowDeploySendMessageRepository flowDeploySendMessageRepository;

    @Autowired
    private FlowEventConfigRepository flowEventConfigRepository;

    /**
     * 创建模型
     */
    @PostMapping("/create")
    public ResponseResult createModel(@RequestBody @Validated ModelCreateVo modelCreateVo) {
        return flowModelRepository.create(modelCreateVo);
    }


    /**
     * 保存模型
     */
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public ResponseResult update(@RequestBody ModelEditVo modelEditVo) {
        ModelSaveVo saveVo;
        try {
            saveVo = JsonUtils.toObject(ModelUtils.getInstance().transformToDBModel(modelEditVo),
                ModelSaveVo.class);
        } catch (Exception e) {
            logger.error("保存流程模型失败", e);
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), e.getMessage());
        }
        Model model = null;
        try {
            model = flowModelRepository.update(saveVo);
        } catch (Exception e) {
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), e.getMessage());
        }
        if (Objects.isNull(model)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "模型不存在");
        }
        ObjectNode modelNode = JsonUtils.getMapper().createObjectNode();
        modelNode.put("modelId", model.getId());
        return ResponseResult.success(modelNode);
    }


    /**
     * 复制模型
     */
    @PostMapping("/copy")
    public ResponseResult copy(@RequestBody @Validated ModelCopyVo modelCopyVo) {
        return flowModelRepository.copy(modelCopyVo);
    }


    /**
     * 流程模型列表-分页
     */
    @PostMapping("/list")
    public ResponseResult list(@RequestBody ModelQueryVo modelQueryVo) {
        return flowModelRepository.list(modelQueryVo);
    }

    /**
     * 流程模型列表-分页(流程委托)
     */
    @PostMapping("/listForEntrust")
    public ResponseResult listForEntrust(@RequestBody ModelQueryVo modelQueryVo) {
        return flowModelRepository.listForEntrust(modelQueryVo);
    }
    /**
     * 版本管理，查看每个模型的在途和完成的实例数量
     * @param modelQueryVo
     * @return
     */
    @PostMapping("/modelDetailList")
    public ResponseResult<List<ModelVersionDetailDTO>>  modelDetailList(@RequestBody ModelQueryVo modelQueryVo){
        return flowModelRepository.getModelVersionDetail(modelQueryVo);
    }

    /**
     * 获取流程模型
     */
    @PostMapping("/detail")
    public ResponseResult<FlowPlatformModelDetailDTO> find(@RequestBody FlowModelQueryVo modelQueryVo) {
        FlowPlatformModelDetailDTO flowPlatformModelDetailDTO = null;
        if (!Objects.isNull(modelQueryVo.getModelId())) {
            flowPlatformModelDetailDTO = flowModelRepository.getModelDetail(modelQueryVo.getModelId());
        } else if (!Objects.isNull(modelQueryVo.getModelCode())) {
            flowPlatformModelDetailDTO = flowModelRepository.getModelDetail(modelQueryVo.getModelId(), modelQueryVo.getModelCode());
        }
        if (Objects.isNull(flowPlatformModelDetailDTO)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "模型不存在");
        }
        return ResponseResult.success(flowPlatformModelDetailDTO);
    }

    /**
     * 获取部署流程模型
     */
    @PostMapping("/deployment/detail")
    public ResponseResult deployment(@RequestBody FlowModelQueryVo modelQueryVo) {
        if (Objects.isNull(modelQueryVo.getDeploymentId())) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "模型部署id为空");
        }
        ObjectNode objectNode = flowModelRepository.getDeploymentDetail(modelQueryVo.getDeploymentId());
        if (Objects.isNull(objectNode)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "部署模型不存在");
        }
        return ResponseResult.success(objectNode);
    }

    /**
     * 部署
     */
    @PostMapping(value = "/deploy")
    public ResponseResult deploy(@RequestBody ProcessModelVo processModel) {
        Deployment deployment;
        try {
            // 部署时，检查流程变量是否都有效（即：流程变量所在的表单是否存在）
            // 20210810：需求转变，内置表单直接清空全部表单(以及表单下变量)。
//            ResponseResult result = flowModelRepository.checkVariableByForm(processModel.getModelId());
//            if (!result.isSuccess()) {
//                logger.error("缺少部分流程变量，拒绝部署：{}", result.getErrMsg());
//                return result;
//            }
            // 部署时，检查第一个用户节点不能设置“强制选人”，否则整个流程无法进行
            // 在部署流程的时候进行检查，提示还有“强制选人”的节点没有被处理到：即无法被任何前序节点进行处理人配置。

            //判断第一个节点处理人留空
            ResponseResult candidateBlankResult = flowModelRepository.checkFirstNodeCandidateBlank(processModel.getModelId());
            if (!candidateBlankResult.isSuccess()) {
                logger.error("第一个用户节点不能留空，拒绝部署：{}", candidateBlankResult.getErrMsg());
                return candidateBlankResult;
            }


            ResponseResult result = flowModelRepository.checkFirstNodeForcePickByForm(processModel.getModelId());
            if (!result.isSuccess()) {
                logger.error("第一个用户节点不能设置强制选人，拒绝部署：{}", result.getErrMsg());
                return result;
            }
            deployment = flowModelRepository.deploy(processModel.getModelId());
            if (Objects.isNull(deployment)) {
                return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "部署失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "部署失败");
        }
        flowDeploySendMessageRepository.flowDeploySendMessage(processModel.getModelId());
        return ResponseResult.success(deployment.getId());
    }

    /**
     * 部署列表
     *
     * @param modelQueryVo
     * @return
     */
    @PostMapping("/deployment/list")
    public ResponseResult deploymentList(@RequestBody ModelQueryVo modelQueryVo) {
        PageInfo<FlowProcdefQueryInfo> pageInfo = new PageInfo<>(flowModelRepository.deploymentList(modelQueryVo));
        return ResponseResult.success(pageInfo.getList()).setTotalCount((int) pageInfo.getTotal())
                .setPageNum(pageInfo.getPageNum()).setPageSize(pageInfo.getPageSize());
    }

    /**
     * 停用流程模型
     *
     * @param offlineModelDto 模型下线dto
     * @return {@link ResponseResult}<{@link Boolean}>
     */
    @PostMapping("/offline")
    public ResponseResult<Boolean> offline(@RequestBody OfflineModelDTO offlineModelDto) {
        String modelId = offlineModelDto.getModelId();
        Model model = repositoryService.getModel(modelId);
        if (model == null) {
            logger.warn("停用模型失败，modelId: "+modelId + " => " + ErrorCode.NOT_EXIST.errMessage());
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), ErrorCode.NOT_EXIST.errMessage());
        }
        if (!flowModelRepository.offlineModel(model.getId())) {
            logger.warn("停用模型失败，modelId: "+modelId);
            return ResponseResult.fail(ErrorCode.OFFLINE_FAILED.errCode(), ErrorCode.OFFLINE_FAILED.errMessage());
        }
        return ResponseResult.success();
    }

    /**
     * 删除流程模型
     *
     * @param
     * @return
     */
    @PostMapping("/delete")
    public ResponseResult delete(@RequestBody DeleteVo deleteVo) {
        Model model = repositoryService.getModel(deleteVo.getModelId());
        if (model == null) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), ErrorCode.NOT_EXIST.errMessage());
        }

        if (!flowModelRepository.deleteModel(model.getId())) {
            return ResponseResult.fail(ErrorCode.DELETE_FAILED.errCode(), ErrorCode.DELETE_FAILED.errMessage());
        }
        return ResponseResult.success();
    }

    /**
     * 获取流程候选人组
     */
    @PostMapping("/candidateGroups")
    public ResponseResult getCandidateGroups(@RequestBody CandidateGroupQueryVo candidateGroupQueryVo) {
        return ResponseResult.success(flowModelRepository.getCandidateGroups(candidateGroupQueryVo.getAppId()));
    }

    /**
     * 获取流程模型变量
     */
    @PostMapping("/getModelVariables")
    public ResponseResult getModelVariables(@RequestBody FlowModelQueryVo modelQueryVo) {
        JsonNode modelVariables = flowModelRepository.getModelVariables(modelQueryVo.getModelId());
        if (modelVariables != null) {
            return ResponseResult.success(modelVariables).setTotalCount(modelVariables.size()).setPageSize(modelVariables.size());
        }
        return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "变量不存在");
    }

    /**
     * 测试链接
     *
     * @param url 请求地址
     * @return 是否连接
     */
    // TODO: 校验下网络是否通，或者提交校验接口 ...
    @GetMapping("/task/testConnection")
    public ResponseResult<Boolean> testConnection(@RequestParam String url) {
//        Map<String, String> params = new HashMap<>(16);
//        params.put("processInstanceId", "test001");
//        params.put("taskId", "test001");

//        TaskEventContext context = TaskEventContext.builder().url(url).body(JSONObject.toJSONString(params)).build();
        return null;
//        return ResponseResult.success(new SendTaskEventToBusinessSystemCommand(context).execute());
    }
    @PostMapping("/CheckUrl")
    public ResponseResult<String> testUrl(@RequestBody String url){
        JSONObject params= JSON.parseObject(url);
        String url1=params.getString("url");
        if(StringUtils.isBlank(url1)){
            return ResponseResult.fail(100,"输入的参数不能为空");
        }
        return flowEventConfigRepository.checkUrl(url1);
    }

    /**
     * 测试配置的事件
     * @param vo
     * @return
     */
    @PostMapping("/testEvent")
    public ResponseResult testEvent(@RequestBody RestTemplateRequestVo vo){
        EventResponse eventResponse=new EventResponse();
        try{
            eventResponse=flowEventConfigRepository.restTemplateRequest(vo.getRequestUrl(),vo.getRequestJsonParams());
        }
        catch(Exception e){
            return ResponseResult.fail(-1,e.getMessage());
        }
        return ResponseResult.success(eventResponse);
    }

    /**
     * 流程导出，json文件
     * @param types 类型：json/xml
     * @param modelId 模型id
     * @param response
     * @return
     */
    @ApiOperation(value = "导出流程模型")
    @GetMapping(value = "/download/{types}")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "types", value = "导出类型：xml/json", required = true, dataType = "String"),
            @ApiImplicitParam(name = "modelId", value = "模型id", required = true, dataType = "String"),
            @ApiImplicitParam(name = "response", value = "服务器的响应，前端不需要提供", required = false, dataType = "HttpServletResponse"),
    })
    public ResponseResult downloadFile(@PathVariable("types") String types,
                             String modelId,
                             HttpServletResponse response) {
        if (Objects.isNull(modelId)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "模型不存在");
        }
        FlowPlatformModelDetailDTO flowPlatformModelDetailDTO = flowModelRepository.getModelDetail(modelId);
        if (flowPlatformModelDetailDTO == null) {
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "获取模型失败");
        }
        // 导出外置表单相关信息
        flowPlatformModelDetailDTO.setFormInfoTransferDTOS(flowModelRepository.findBindExternalFormWithModel(modelId));

        // 导出下载流程文件
        if ("json".equals(types)) {
            // 文件命名：模型编码.模型名称.json
            String fileName = String.format("%s.%s.json",
                    flowPlatformModelDetailDTO.getKey(),
                    flowPlatformModelDetailDTO.getName());
            // data部分是实际的模型内容
            byte[] datas = JsonUtils.toJson(flowPlatformModelDetailDTO).getBytes(StandardCharsets.UTF_8);
            // 拼装返回的数据流
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setStatus(200);
            response.addHeader("Content-Length", "" + datas.length);
            response.setContentType("application/octet-stream");
            try {
                response.addHeader("Content-Disposition",
                        "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
                outputStream.write(datas);
                outputStream.flush();
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
                return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "下载失败");
            }
        } else if ("xml".equals(types)) {
            // TODO:  导出xml
        }
        return null;
    }

    /**
     * 流程导入
     * @param types 类型：json/xml
     * @param modelUploadDTO 表单+文件的VO
     * @return
     */
    @PostMapping("/upload/{types}")
    @ApiOperation(value = "导入流程模型")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "types", value = "导入类型：xml/json", required = true, dataType = "String"),
            @ApiImplicitParam(name = "modelUploadDTO", value = "表单+文件的VO", required = true, dataType = "String"),
    })
    public ResponseResult uploadFile(@PathVariable("types") String types, @ModelAttribute ModelUploadDTO modelUploadDTO) {
        modelUploadDTO.setName(EncodingUtil.convertToUtf8(modelUploadDTO.getName()));
        modelUploadDTO.setDescription(EncodingUtil.convertToUtf8(modelUploadDTO.getDescription()));
        try {
            if ("json".equals(types)) {
                // 解析文件并新建流程配置
                Map.Entry<ResponseResult, FlowPlatformModelDetailDTO> result = flowModelRepository.uploadJson(modelUploadDTO);
                if (!result.getKey().isSuccess()) {
                    return result.getKey();
                }
                // 开始做更新
                FlowPlatformModelDetailDTO detailDTO = result.getValue();
                ModelEditVo editVo = new ModelEditVo();
                editVo.setAppId(detailDTO.getAppId());
                editVo.setBusiId(detailDTO.getBusiId());
                editVo.setKey(detailDTO.getKey());
                editVo.setName(detailDTO.getName());
                editVo.setModelId(detailDTO.getModelId());
                editVo.setModelJson((ProcessModelVo) detailDTO.getModelJson());
                editVo.setDescription(detailDTO.getDescription());
                ResponseResult updateResult = update(editVo);

                // update失败的时候，要返回失败的信息，因为更重要
                if (!updateResult.isSuccess()) {
                    return updateResult;
                }
                // update成功，且无警告
                else if (result.getKey().getErrCode()==0){
                    updateResult.setErrMsg("导入成功");
                }
                // update成功，但有警告
                else {
                    updateResult.setErrCode(result.getKey().getErrCode());
                    updateResult.setErrMsg(result.getKey().getErrMsg());
                }
                return updateResult;
            }else if ("xml".equals(types)) {
                // TODO:  导入xml
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "文件解析失败");
        }
        return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "不支持的类型");
    }


    /**
     * 流程模型列表
     */
    @PostMapping("/getModelList")
    public ResponseResult getModelList(@RequestBody ModelQueryNoPageVo modelQueryNoPageVo) {
        logger.info("getModelList:" + JSONObject.toJSONString(modelQueryNoPageVo));
        return flowModelRepository.getModelList(modelQueryNoPageVo);
    }

    @PostMapping("/checkModelExist")
    public ResponseResult checkModelExist(@RequestParam String modelName, @RequestParam String modelKey) {
        return ResponseResult.success(flowModelRepository.checkModelExist(modelName, modelKey));
    }

    /**
     * 获取流程模型事件
     */
    @PostMapping("/modelEvents")
    public ResponseResult getModelEvents(@RequestBody ModelEventTempQuery modelEventTempQuery){
       return flowModelRepository.getModelEvents(modelEventTempQuery);
    }

    /**
     * 更新流程模型事件到事件临时表
     */
    @PostMapping("/modelEventsToTemp")
    public ResponseResult modelEventsToTemp(){
        return flowModelRepository.setModelEventsToTemp();
    }

}
